package cn.xd.security.provider;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.xd.common.constant.AuthConstant;
import cn.xd.common.exception.BizException;
import cn.xd.security.token.AdminUserCaptchaToken;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
@Slf4j
public class AdminUserCaptchaProvider extends DaoAuthenticationProvider {

    @Resource(name = "AdminUserDetailServiceImpl")
    private UserDetailsService userDetailsService;
    @Resource(name = "PasswordEncoder")
    private PasswordEncoder passwordEncoder;
    @Resource
    private RedisTemplate<String, String> redisTemplate;


    //构造方法注入MyUserDetailsService和MyPasswordEncoder
    @Override
    public void doAfterPropertiesSet() {
        setPasswordEncoder(passwordEncoder);
        setUserDetailsService(userDetailsService);
    }

    @Override
    public Authentication authenticate(Authentication authentication) {
        log.info("AdminUserCaptchaProvider");

        AdminUserCaptchaToken adminUserCaptchaToken = (AdminUserCaptchaToken) authentication;
        String key = String.format(AuthConstant.freezeFormat, adminUserCaptchaToken.getType().getAppTypeEnum().getCode(), StrUtil.subBefore(adminUserCaptchaToken.getName(), "@", Boolean.TRUE));
        Object freezeObj = redisTemplate.opsForValue().get(key);
        if (ObjUtil.isNotEmpty(freezeObj) && freezeObj instanceof Date freezeDate) {
            throw new BizException(String.format("密码输错次数过多,账号已被冻结，请五分钟后再尝试。冻结时间：" + DateUtil.format(freezeDate, "yyyy-MM-dd HH:mm:ss")));
        }

        // 获取参数中的验证码
        String verifyCode = adminUserCaptchaToken.getVerifyCode();
        if (ObjUtil.isEmpty(verifyCode)) {
            throw new BizException("verifyCode cannot be empty.");
        }

        String verifyCodeKey = adminUserCaptchaToken.getVerifyKey();
        if (ObjUtil.isEmpty(verifyCodeKey)) {
            throw new BizException("verifyCodeKey cannot be empty.");
        }
        String validateCodeKey = AuthConstant.VERIFY_CODE_KEY_PREFIX + verifyCodeKey;

        // 从缓存取出正确的验证码和用户输入的验证码比对
        String correctValidateCode = redisTemplate.opsForValue().get(validateCodeKey);
        if (StrUtil.isBlank(correctValidateCode)) {
            throw new BizException("验证码已过期，请重新获取验证码！");
        }
        if (!verifyCode.equalsIgnoreCase(correctValidateCode)) {
            throw new BizException("您输入的验证码不正确！");
        }

        // 验证码验证通过，删除 Redis 的验证码
        redisTemplate.delete(validateCodeKey);
        return super.authenticate(authentication);
    }

    /**
     * 判断只有传入UserAuthenticationToken的时候才使用这个Provider
     * supports会在AuthenticationManager层被调用
     *
     * @param authentication
     * @return
     */
    public boolean supports(Class<?> authentication) {
        return AdminUserCaptchaToken.class.isAssignableFrom(authentication);
    }
}
